package com.xiaoxin.experience.system;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.spec.KeySpec;
import java.util.Base64;

/**
 * @author Beowulf
 */
public class SecretKeyResolver
{

    private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";

    /**
     * 生成密文的长度
     */
    private static final int HASH_SIZE = 32;

    /**
     * 迭代次数
     */
    private static final int PBKDF2_ITERATIONS = 1000;

    private SecretKeyResolver()
    {

    }

    public static String resolveSecretKey(String keyIv, String workingKey, String workingIv,
        String firstComponent, String secondComponent, String thirdComponent)
    {
        String secretKey = null;
        if (StringUtils.isNotBlank(workingKey) && StringUtils.isNotBlank(workingIv))
        {
            String keyMaterial = resolveKeyMat(firstComponent, secondComponent, thirdComponent);
            Assert.hasText(keyIv, "secret key iv not config.");
            Assert.hasText(keyMaterial, "secret key material not config.");
            byte[] rootKey = deriveRootKey(keyMaterial, keyIv);
            secretKey = decryptWorkingKey(workingKey, rootKey, workingIv);
        }
        return secretKey;
    }


    private static byte[] deriveRootKey(String keyingMaterial, String iv)
    {
        try
        {
            byte[] bytes = Base64.getDecoder().decode(iv);
            KeySpec spec = new PBEKeySpec(keyingMaterial.toCharArray(), bytes, PBKDF2_ITERATIONS, HASH_SIZE * 4);
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
            return secretKeyFactory.generateSecret(spec).getEncoded();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    private static String decryptWorkingKey(String workingKey, byte[] rootKey, String iv)
    {
        try
        {
            Key skeySpec = new SecretKeySpec(rootKey, "AES");
            //使用GCM模式，需要一个向量iv，可增加加密算法的强度
            byte[] ivBytes = Base64.getDecoder().decode(iv);
            GCMParameterSpec ips = new GCMParameterSpec(128, ivBytes);

            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ips);
            byte[] result = cipher.doFinal(Base64.getDecoder().decode(workingKey));
            return Base64.getEncoder().encodeToString(result);
        }
        catch (Exception e)
        {
            return null;
        }
    }

    public static String resolveKeyMat(String keyComponent1, String keyComponent2, String keyComponent3)
    {
        Assert.hasText(keyComponent1, "key component 1 not config.");
        Assert.hasText(keyComponent2, "key component 2 not config.");
        Assert.hasText(keyComponent3, "key component 3 not config.");
        try
        {
            byte[] componentBytes1 = Base64.getDecoder().decode(keyComponent1);
            byte[] componentBytes2 = Base64.getDecoder().decode(keyComponent2);
            byte[] componentBytes3 = Base64.getDecoder().decode(keyComponent3);
            byte[] keyIvBytes = xorWithKey(xorWithKey(componentBytes1, componentBytes2), componentBytes3);
            return Base64.getEncoder().encodeToString(keyIvBytes);
        }
        catch (Exception e)
        {
            return null;
        }
    }

    /**
     * 两个字符串异或
     *
     * @param a
     * @param key
     * @return
     */
    private static byte[] xorWithKey(byte[] a, byte[] key)
    {
        byte[] out = new byte[a.length];
        for (int i = 0; i < a.length; i++)
        {
            out[i] = (byte)(a[i] ^ key[i % key.length]);
        }
        return out;
    }
}
